package com.hk.core.data.jpa.repository;

import com.hk.commons.query.OrderUtils;
import com.hk.commons.query.page.QueryPage;
import com.hk.commons.query.page.SimpleQueryPage;
import com.hk.commons.util.CollectionUtils;
import com.hk.commons.util.FieldUtils;
import com.hk.commons.util.ObjectUtils;
import com.hk.commons.util.StringUtils;
import com.hk.core.jdbc.query.ConditionQueryModel;
import com.hk.core.jdbc.query.Parametric;
import jakarta.persistence.EntityManager;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Kevin
 */
public class BaseSimpleJpaRepository<T extends Persistable<ID>, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> {

    private final JpaEntityInformation<T, ?> entityInformation;

    private final EntityManager entityManager;

    public BaseSimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.entityInformation = entityInformation;
    }

    public BaseSimpleJpaRepository(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
        this.entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, em);
    }

    @Override
    public QueryPage<T> queryForPage(ConditionQueryModel queryModel) {
        var param = queryModel.getParam();
        var parameters = new ArrayList<>();
        var whereString = param.toSqlString(Parametric.PARAMETERIZATION_INDEX, parameters);
        var entityName = entityInformation.getEntityName();
        var hql = new StringBuilder("FROM ");
        var sqlCount = new StringBuilder("SELECT COUNT(")
                .append(entityInformation.getRequiredIdAttribute().getName())
                .append(") FROM ")
                .append(entityName);
        hql.append(entityName);
        if (StringUtils.hasText(whereString)) {
            hql.append(" WHERE ").append(whereString);
            sqlCount.append(" WHERE ").append(whereString);
        }
        var orders = queryModel.getOrders();
        var fieldNames = FieldUtils.getAllFieldsList(entityInformation.getJavaType()).stream()
                .map(Field::getName)
                .collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(orders)) {
            var orderList = orders.stream()
                    .filter(item -> fieldNames.contains(item.getField()))
                    .collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(orderList)) {
                hql.append(OrderUtils.toOrderSql(orderList));
            }
        }
        var countQuery = entityManager.createQuery(sqlCount.toString(), Long.class); // 分页查询
        for (int index = 0, size = parameters.size(); index < size; index++) {
            var value = parameters.get(index);
            countQuery.setParameter(index + 1, value);
        }
        long count = ObjectUtils.defaultIfNull(countQuery.getSingleResult(), 0L);
        if (count == 0) {
            //当查询记录为 0 时，不再需要查询数据集
            return new SimpleQueryPage<>(new ArrayList<>(), count, queryModel.getStartRowIndex() + 1,
                    queryModel.getPageSize());
        }
        var hqlQuery = entityManager.createQuery(hql.toString(), entityInformation.getJavaType())
                .setFirstResult(queryModel.getStartRowIndex() * queryModel.getPageSize()) // 查询记录数
                .setMaxResults(queryModel.getPageSize());
        for (int index = 0, size = parameters.size(); index < size; index++) {
            var value = parameters.get(index);
            hqlQuery.setParameter(index + 1, value);
        }
        return new SimpleQueryPage<>(hqlQuery.getResultList(), count,
                queryModel.getStartRowIndex() + 1, queryModel.getPageSize());
    }

    @Override
    public void deleteById(ID id) {
        if (Objects.nonNull(id)) {
            super.deleteById(id);
        }
    }
}
